function existence(obj) {
    return obj != null;
}

function countBy(arr, fn) {
    return arr.reduce((pre, cur) => {
        const data = fn(cur);

        if (existence(pre[data])) {
            pre[data]++;
        } else {
            pre[data] = 1;
        }

        return pre;
    }, []);
}

function curry2(fn) {
    return (secondArg) => {
        return (firstArg) => {
            return fn(firstArg, secondArg);
        };
    };
}

function singToString(song) {
    return [song.artist, song.track].join(' - ');
}

const arr = [
    {artist: 'Burial', track: 'Archangel'},
    {artist: 'Burial', track: 'Archangel'},
    {artist: 'Burial', track: 'Archangel'},
    {artist: 'A', track: 'Archangel'},
    {artist: 'A', track: 'Archangel'},
    {artist: 'B', track: 'Archangel'}
];

countBy(arr,singToString);

const songCount = curry2(countBy)(singToString);
songCount(arr);

function curry3(fn) {
    return (last) => {
        return (middle) => {
            return (first) => {
                return fn(first, middle, last);
            };
        };
    };
}

function toHex(n) {
    const hex = n.toString(16);
    return (hex.length < 2) ? ['0', hex].join('') : hex;
}

function rgbToHexString(r, g, b) {
    return ['#', toHex(r), toHex(g), toHex(b)].join('');
}

rgbToHexString(255, 255, 255);
let blueGreenish = curry3(rgbToHexString)(255)(200);
blueGreenish(0);

function compose(...fns) {
    const size = fns.length;
    return (...args) => {
        let arg = fns[size - 1] && fns[size - 1](...args);

        for (let i = fns.length - 2; i >= 0; i--) {
            const fn = fns[i];
            arg = fn(arg);
        }

        return arg;
    };
}

function partial(fn, ...args1) {
    return (...args2) => {
        return fn(...args1, ...args2);
    };
}